﻿using System.Collections;
using System.Threading.Tasks;
using System.Collections.Generic;
using UnityEngine;

public class MapData
{
    public Vector3[] Vertices;
    public int[] Triangles;
    public Vector2[] UVs;
}
public static partial class HexagnoalMapCreator
{
    //区分两个不同的点
    class Vector3Comparer : IEqualityComparer<Vector3>
    {
        float eslion;
        public Vector3Comparer(float eslion)
        {
            this.eslion = eslion;
        }
        public bool Equals(Vector3 x, Vector3 y)
        {
            return (x - y).sqrMagnitude < eslion;
        }

        public int GetHashCode(Vector3 obj)
        {
            return obj.GetHashCode();
        }
    }
    //角朝上 数组里面个点的按顺时针排列
    static Vector3[] GetConcerConcerUp(float innerRadius, float outerRadius)
    {
        Vector3[] concerOffest = new Vector3[6];
        concerOffest[0] = new Vector3(0, 0, -outerRadius); //下
        concerOffest[1] = new Vector3(-innerRadius, 0, -outerRadius / 2f); //左下
        concerOffest[2] = new Vector3(-innerRadius, 0, outerRadius / 2f); //左上
        concerOffest[3] = new Vector3(0, 0, outerRadius); //上
        concerOffest[4] = new Vector3(innerRadius, 0, outerRadius / 2f); //右上
        concerOffest[5] = new Vector3(innerRadius, 0, -outerRadius / 2f); //右下
        return concerOffest;
    }
    //边朝上 数组里面个点的按顺时针排列，且与GetConcerOffest存在对应关系
    static Vector3[] GetConcerEdgeUp(float innerRadius, float outerRadius)
    {
        Vector3[] concer = new Vector3[6];
        concer[0] = new Vector3(-outerRadius, 0, 0); //左
        concer[1] = new Vector3(-outerRadius / 2f, 0, innerRadius); //左上
        concer[2] = new Vector3(outerRadius / 2f, 0, innerRadius); //右上
        concer[3] = new Vector3(outerRadius, 0, 0); //右
        concer[4] = new Vector3(outerRadius / 2f, 0, -innerRadius); //右下
        concer[5] = new Vector3(-outerRadius / 2f, 0, -innerRadius); //左下
        return concer;
    }
    static Vector3[] GetConcerStep(float innerRadius, float outerRadius)
    {
        Vector3[] step = new Vector3[6];
        step[0] = new Vector3(innerRadius, 0, outerRadius * 1.5f); //左，向右上方叠加
        step[1] = new Vector3(innerRadius * 2f, 0, 0); //左上，向右叠加
        step[2] = new Vector3(innerRadius, 0, -outerRadius * 1.5f); //右上，向右下叠加
        step[3] = new Vector3(-innerRadius, 0, -outerRadius * 1.5f); //右，向左下叠加
        step[4] = new Vector3(-innerRadius * 2f, 0, 0); //右下，向左叠加
        step[5] = new Vector3(-innerRadius, 0, outerRadius * 1.5f); //左下，向左上叠加
        return step;
    }/// <summary>
     /// 六边形的边与自身坐标系的Z轴平行，Z轴经过其中一顶点，每行之间交错排列，这不是最高效的创建方式，但它是能保留每个六边形的顶点信息的网格形式中最高效的，行数和列数过大会出现错误。不推荐创建这种形式的地图
     /// </summary>
     /// <param name="rowCellCount">地图行单元格数</param>
     /// <param name="columnCellCount">地图列单元格数</param>
     /// <param name="outerRadius">六边形单元格外接圆半径</param>
     /// <param name="mapData">地图信息</param>
    static void CreateHexagnoalMap(int rowCellCount, int columnCellCount, float outerRadius,MapData mapData)
    {
        float innerRaidus = outerRadius * 0.866025404f;//内接圆半径
        int edgeVerRowCnt = rowCellCount * 2; //顶点行的第一行和最后一行只有columnCount个顶点，所以特别处理，除这两行外的顶点行有这么多行
        int edgeColCnt = columnCellCount + 1;//顶点行中除去第一行和最后一行的其它行每行的顶点个数
        float colstep = innerRaidus * 2f; //一行中相邻顶点间的距离
        float rowstep = outerRadius * 3f / 4f; //因为不是依行下标递增计算每行顶点坐标的，所以要这样算；因为i每次递增4，所以要除4
        //容易推得，所有这种形式的六边形格子组成的地图的顶点数的计算公式如下
        int verCnt = columnCellCount * 2 + rowCellCount * edgeColCnt * 2;
        Vector3[] vertices = new Vector3[verCnt];
        int i, j, jmul12;
        #region 计算顶点坐标
        //处理第一行顶点
        for (i = 0; i < columnCellCount; i++)
            vertices[i] = new Vector3(i * colstep, 0f, -outerRadius);
        //处理六边形下标为偶数的那一行六边形的第一个边顶点
        for (i = 0; i < edgeVerRowCnt; i += 4)
            for (j = 0; j < edgeColCnt; j++)
                vertices[columnCellCount + i * edgeColCnt + j] = new Vector3(-innerRaidus + colstep * j, 0f, -outerRadius / 2f + rowstep * i);
        //处理六边形下标为偶数的那一行六边形的第二个边顶点
        for (i = 1; i < edgeVerRowCnt; i += 4)
            for (j = 0; j < edgeColCnt; j++)
                vertices[columnCellCount + i * edgeColCnt + j] = new Vector3(-innerRaidus + colstep * j, 0f, outerRadius / 2f + rowstep * (i - 1));
        //处理"六边形下标为奇数"的那一行六边形的第一个边顶点
        for (i = 2; i < edgeVerRowCnt; i += 4)
            for (j = 0; j < edgeColCnt; j++)
                vertices[columnCellCount + i * edgeColCnt + j] = new Vector3(colstep * j, 0f, outerRadius + rowstep * (i - 2));
        //处理"六边形下标为奇数"第二个边顶点
        for (i = 3; i < edgeVerRowCnt; i += 4)
            for (j = 0; j < edgeColCnt; j++)
                vertices[columnCellCount + i * edgeColCnt + j] = new Vector3(colstep * j, 0f, outerRadius * 2f + rowstep * (i - 3));
        //处理最后一行顶点
        bool flag = (rowCellCount & 1) == 0;//是否为偶数行
        float offestC = flag ? innerRaidus : 0f;
        float z;
        if (flag)
        {
            int tmp = (rowCellCount - 2) / 2 * 3;
            z = (2.5f + tmp) * outerRadius;
        }
        else
        {
            int tmp = rowCellCount / 2 * 3;
            z = outerRadius + tmp * outerRadius;
        }
        int ustartIdx = verCnt - columnCellCount;
        for (i = 0; i < columnCellCount; i++)
            vertices[ustartIdx + i] = new Vector3(offestC + i * colstep, 0f, z);
        #endregion
        #region 设置三角形
        int[] triangles = new int[rowCellCount * columnCellCount * 12];
        int veri = 0;
        int trii = 0;

        //第一行六边形要特殊处理
        for (j = 0; j < columnCellCount; j++)
        {
            jmul12 = j * 12;
            triangles[trii + jmul12] = triangles[trii + jmul12 + 3] = triangles[trii + jmul12 + 6] = triangles[trii + jmul12 + 9] = (veri * edgeColCnt) + j;

            triangles[trii + jmul12 + 1] = ((veri + 1) * edgeColCnt) + j - 1;

            triangles[trii + jmul12 + 2] = triangles[trii + jmul12 + 4] = ((veri + 2) * edgeColCnt) + j - 1;

            triangles[trii + jmul12 + 5] = triangles[trii + jmul12 + 7] = ((veri + 3) * edgeColCnt) + j - 1;

            triangles[trii + jmul12 + 8] = triangles[trii + jmul12 + 10] = ((veri + 2) * edgeColCnt) + j;

            triangles[trii + jmul12 + 11] = ((veri + 1) * edgeColCnt) + j;

        }
        //奇数行和偶数行之间也有区别
        for (i = 2; i < rowCellCount; i += 2)
        {
            veri = i * 2;
            trii = i * columnCellCount * 12;
            for (j = 0; j < columnCellCount; j++)
            {
                jmul12 = j * 12;
                triangles[trii + jmul12] = triangles[trii + jmul12 + 3] = triangles[trii + jmul12 + 6] = triangles[trii + jmul12 + 9] = (veri * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 1] = ((veri + 1) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 2] = triangles[trii + jmul12 + 4] = ((veri + 2) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 5] = triangles[trii + jmul12 + 7] = ((veri + 3) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 8] = triangles[trii + jmul12 + 10] = ((veri + 2) * edgeColCnt) + j;

                triangles[trii + jmul12 + 11] = ((veri + 1) * edgeColCnt) + j;

            }
        }
        for (i = 1; i < rowCellCount - 1; i += 2)
        {
            veri = i * 2;
            trii = i * columnCellCount * 12;
            for (j = 0; j < columnCellCount; j++)
            {
                jmul12 = j * 12;
                triangles[trii + jmul12] = triangles[trii + jmul12 + 3] = triangles[trii + jmul12 + 6] = triangles[trii + jmul12 + 9] = (veri * edgeColCnt) + j;

                triangles[trii + jmul12 + 1] = ((veri + 1) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 2] = triangles[trii + jmul12 + 4] = ((veri + 2) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 5] = triangles[trii + jmul12 + 7] = ((veri + 3) * edgeColCnt) + j;

                triangles[trii + jmul12 + 8] = triangles[trii + jmul12 + 10] = ((veri + 2) * edgeColCnt) + j;

                triangles[trii + jmul12 + 11] = ((veri + 1) * edgeColCnt) + j;
            }
        }
        //在某些条件下也要考虑最后一行，如果是奇数行的话最后一行下标就是偶数，这时最后一行的情况并不特殊
        if ((rowCellCount & 1) == 0)
        {
            veri = (rowCellCount - 1) * 2;
            trii = veri * columnCellCount * 6;
            for (j = 0; j < columnCellCount; j++)
            {
                jmul12 = j * 12;
                triangles[trii + jmul12] = triangles[trii + jmul12 + 3] = triangles[trii + jmul12 + 6] = triangles[trii + jmul12 + 9] = (veri * edgeColCnt) + j;

                triangles[trii + jmul12 + 1] = ((veri + 1) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 2] = triangles[trii + jmul12 + 4] = ((veri + 2) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 5] = triangles[trii + jmul12 + 7] = ((veri + 3) * edgeColCnt) + j - 1;

                triangles[trii + jmul12 + 8] = triangles[trii + jmul12 + 10] = ((veri + 2) * edgeColCnt) + j;

                triangles[trii + jmul12 + 11] = ((veri + 1) * edgeColCnt) + j;
            }
        }
        #endregion
        mapData.Triangles = triangles;
        mapData.Vertices = vertices;
    }
    /// <summary>
    /// 六边形单元格组成一个大六边形，这不是最高效的创建方式，但它是能保留每个六边形的顶点信息的网格形式中最高效的，edgeCellCount太大会出现错误，大约100左右会出问题，这时顶点数和三角形数过多，Unity绘制出错
    /// </summary>
    /// <param name="edgeCellCount">大六边形的边的单元格个数</param>
    /// <param name="outerRadius">六边形单元格外接圆半径</param>
    /// <param name="mapData">地图信息</param>
    public static void CreateHexagnoalMap(int edgeCellCount,float outerRadius, MapData mapData)
    {
        int lenCount = (edgeCellCount << 1) - 1;
        int cellCount = (3 * lenCount - 1) * (lenCount - 1) / 4 + lenCount;
        Dictionary<Vector3, int> dictionary = new Dictionary<Vector3, int>(cellCount * 3, new Vector3Comparer(outerRadius / 15f));
        float innerRadius = outerRadius * 0.866025404f;
        int half = lenCount >> 1;
        int x, y, z, endy = 0;
        Vector3 hexPos;
        float onefiveor = 1.5f * outerRadius;

        Vector3[] concerOffest = GetConcerConcerUp(innerRadius, outerRadius);

        Vector3[] hexConcers = new Vector3[6];
        int[] hexConcerIdxs = new int[6];

        int[] triangles = new int[cellCount * 12];
        int i, tragleIdx = 0;
        for (x = -half; x <= 0; x++)
        {
            for (y = half; y >= endy; y--)
            {
                z = -x - y;
                hexPos = new Vector3((x - y) * innerRadius, 0f, z * onefiveor);
                for (i = 0; i < 6; i++)//设置顶点
                {
                    hexConcers[i] = hexPos + concerOffest[i];
                    //计算顶点索引
                    if (!dictionary.TryGetValue(hexConcers[i], out hexConcerIdxs[i]))
                    {
                        hexConcerIdxs[i] = dictionary.Count;
                        dictionary.Add(hexConcers[i], hexConcerIdxs[i]);
                    }
                }
                for (i = 1; i < 5; i++)//设置三角形
                {
                    triangles[tragleIdx++] = hexConcerIdxs[0];
                    triangles[tragleIdx++] = hexConcerIdxs[i];
                    triangles[tragleIdx++] = hexConcerIdxs[i + 1];
                }
            }
            endy--;
        }
        endy = half - 1;
        for (x = 1; x <= half; x++)
        {
            for (y = -half; y <= endy; y++)
            {
                z = -x - y;
                hexPos = new Vector3((x - y) * innerRadius, 0f, z * onefiveor);
                for (i = 0; i < 6; i++)//设置顶点
                {
                    hexConcers[i] = hexPos + concerOffest[i];
                    //计算顶点索引
                    if (!dictionary.TryGetValue(hexConcers[i], out hexConcerIdxs[i]))
                    {
                        hexConcerIdxs[i] = dictionary.Count;
                        dictionary.Add(hexConcers[i], hexConcerIdxs[i]);
                    }
                }
                for (i = 1; i < 5; i++)//设置三角形
                {
                    triangles[tragleIdx++] = hexConcerIdxs[0];
                    triangles[tragleIdx++] = hexConcerIdxs[i];
                    triangles[tragleIdx++] = hexConcerIdxs[i + 1];
                }
            }
            endy--;
        }
        Vector3[] vertices = new Vector3[dictionary.Count];
        float width = innerRadius * 2f * (edgeCellCount * 2 - 1);//地图包围矩形的宽
        float height = outerRadius * (edgeCellCount * 3 - 1);//地图包围矩形的高
        float halfWidth = width / 2f, halfHeight = height / 2f;
        Vector2[] uvs = new Vector2[vertices.Length];
        Vector3 vertex;
        foreach (var item in dictionary)//设置顶点
        {
            vertex = item.Key;
            vertices[item.Value] = vertex;
            uvs[item.Value] = new Vector2((vertex.x + halfWidth) / width, (vertex.z + halfHeight) / height);
        }
        mapData.Triangles = triangles;
        mapData.Vertices = vertices;
        mapData.UVs = uvs;
    }

    /// <summary>
    ///如果不想保存六边形网格地图中每个单元格网格信息的话，用这个方法，该方法时空效率高，把地图划分为尽可能少的三角形，edgeCellCount过大会出错（不超过5168即可），划分方法见六边形地图高效的三角形划分方式.jpg
    /// </summary>
    /// <param name="edgeCellCount">大六边形的边的单元格个数</param>
    /// <param name="outerRadius">六边形单元格外接圆半径</param>
    /// <param name="mapData">地图信息</param>
    public static void CreateHexagnoalMapFast(int edgeCellCount, float outerRadius, MapData mapData)
    {
        float innerRadius = outerRadius * 0.866025404f;

        float bigOuterRadius = innerRadius * (2f / 3f + (edgeCellCount - 1) * 2);
        //六边形地图的角单元格的非相邻角连线的交点中离地图中心点最远的那些点
        Vector3[] bigConcer = GetConcerEdgeUp(bigOuterRadius * 0.866025404f, bigOuterRadius);

        Vector3[] concerOffest = GetConcerConcerUp(innerRadius, outerRadius);
        float cellConcerOuterRadius = innerRadius * (edgeCellCount - 1) * 2;
        //六边形地图的角单元格的中心点坐标
        Vector3[] concerCellPositions = GetConcerEdgeUp(cellConcerOuterRadius * 0.866025404f, cellConcerOuterRadius);
        //六边形地图的角单元格的偏移量，用于获取边上单元格的位置坐标
        Vector3[] concerStep = GetConcerStep(innerRadius, outerRadius);
        Dictionary<Vector3, int> dictionary = new Dictionary<Vector3, int>(edgeCellCount * 6, new Vector3Comparer(outerRadius / 15f));
        int triIdx = 0;
        int[] triangles = new int[edgeCellCount * 18 + 12];
        int i, j, k, temp, n;
        int stepCnt = edgeCellCount - 2;
        Vector3 tmpVertex, tempCellPos;
        for (i = 0; i < 6; i++)
        {
            triangles[triIdx++] = dictionary.Count;
            dictionary.Add(bigConcer[i], dictionary.Count);
            //temp的设置与bigConcer、concerCellPositions、concerOffest之间元素在数组中的位置有关
            temp = i + 4;
            for (j = i; j < temp; j++)
            {
                tmpVertex = concerCellPositions[i] + concerOffest[j % 6];
                if (!dictionary.TryGetValue(tmpVertex, out k))
                {
                    triangles[triIdx++] = dictionary.Count;
                    dictionary.Add(tmpVertex, dictionary.Count);
                }
                else
                    triangles[triIdx++] = k;
            }
            triangles[triIdx] = triangles[triIdx - 3]; //设置回去
            triIdx++;
            tempCellPos = concerCellPositions[i];
            for (j = 0; j < stepCnt; j++)
            {
                tempCellPos += concerStep[i];
                for (n = i + 1; n < temp; n++)
                {
                    tmpVertex = tempCellPos + concerOffest[n % 6];
                    if (!dictionary.TryGetValue(tmpVertex, out k))
                    {
                        triangles[triIdx++] = dictionary.Count;
                        dictionary.Add(tmpVertex, dictionary.Count);
                    }
                    else
                        triangles[triIdx++] = k;
                }
            }
        }
        for (i = 1; i < 5; i++) //绘制最大的六边形
        {
            triangles[triIdx++] = dictionary[bigConcer[0]];
            triangles[triIdx++] = dictionary[bigConcer[i]];
            triangles[triIdx++] = dictionary[bigConcer[i + 1]];
        }
        Vector3[] vertices = new Vector3[dictionary.Count];
        float width = innerRadius * 2f * (edgeCellCount * 2 - 1);//地图包围矩形的宽
        float height = outerRadius * (edgeCellCount * 3 - 1);//地图包围矩形的高
        float halfWidth = width / 2f, halfHeight = height / 2f;
        Vector2[] uvs = new Vector2[vertices.Length];
        Vector3 vertex;
        foreach (var item in dictionary)//设置顶点
        {
            vertex = item.Key;
            vertices[item.Value] = vertex;
            uvs[item.Value] = new Vector2((vertex.x + halfWidth) / width, (vertex.z + halfHeight) / height);
        }
        mapData.Triangles = triangles;
        mapData.Vertices = vertices;
        mapData.UVs = uvs;
    }
    //异步创建网格
    public static async void CreateHexagnoalMapAsync(int rowCount, int columnCount, float outerRadius, MeshFilter meshFilter)
    {
        Mesh mesh = new Mesh();
        MapData mapData = new MapData();
        await Task.Run(() => { CreateHexagnoalMap(rowCount, columnCount, outerRadius, mapData); });
        mesh.vertices = mapData.Vertices;
        mesh.triangles = mapData.Triangles;
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
        meshFilter.mesh = mesh;
    }
    //异步创建网格，meshdata不需要赋值
    public static async Task<Mesh> CreateHexagnoalMapAsync(int edgeCellCount, float outerRadius, Material material)
    {   
        Mesh mesh = new Mesh();
        MapData mapData = new MapData();
        await Task.Run(() => CreateHexagnoalMap(edgeCellCount, outerRadius, mapData));
        mesh.vertices = mapData.Vertices;
        mesh.triangles = mapData.Triangles;
        mesh.uv = mapData.UVs;
        mesh.RecalculateTangents();
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
        return mesh;
    }
}